///// --------> ext base
#include "common.hpp"
#include <cerrno>
#include <ruby.h>

constexpr size_t MB = 1024 * 1024;

void enhance_exception_raise() {
  VALUE err_obj;
  auto msg = strerror(errno);
  err_obj = rb_exc_new2(rb_eSystemCallError, msg);
  rb_exc_raise(err_obj);
}

// default Ruby string encoding is ASCII-8BIT
static VALUE enhance_detect_buffer_encoding(VALUE obj, VALUE str) {
  (void)obj;
  auto len = RSTRING_LEN(str);
  auto buf = reinterpret_cast<const unsigned char *>(RSTRING_PTR(str));
  auto t = gitee::DetectBufferEncoding(buf, static_cast<size_t>(len));
  constexpr struct {
    gitee::encoding_t e;
    const char *p;
    std::size_t len;
  } em[] = {
      // table begin
      {gitee::encoding_t::binary, "ASCII-8BIT", sizeof("ASCII-8BIT") - 1},
      {gitee::encoding_t::utf7, "UTF-7", sizeof("UTF-7") - 1},
      {gitee::encoding_t::utf8, "UTF-8", sizeof("UTF-8") - 1},
      {gitee::encoding_t::utf8withbom, "UTF-8", sizeof("UTF-8") - 1},
      {gitee::encoding_t::utf16le, "UTF-16LE", sizeof("UTF-16LE") - 1},
      {gitee::encoding_t::utf16be, "UTF-16BE", sizeof("UTF-16BE") - 1},
      {gitee::encoding_t::utf32le, "UTF-32LE", sizeof("UTF-32LE") - 1},
      {gitee::encoding_t::utf32be, "UTF-32BE", sizeof("UTF-32BE") - 1}
      // table end
  };
  for (auto &x : em) {
    if (x.e == t) {
      return rb_str_new(x.p, x.len);
    }
  }
  return Qnil;
}

static VALUE enhance_repo_size(VALUE obj, VALUE str) {
  (void)obj;
  Check_Type(str, T_STRING);
  auto len = RSTRING_LEN(str);
  gitee::string_view sv{RSTRING_PTR(str),
                        static_cast<gitee::string_view::size_type>(len)};
  int64_t size = 0;
  if (!gitee::CalculateRepoSize(sv, size)) {
    return Qnil;
  }
  return LL2NUM(size);
}

static VALUE enhance_repo_size_mb(VALUE obj, VALUE str) {
  (void)obj;
  Check_Type(str, T_STRING);
  auto len = RSTRING_LEN(str);
  gitee::string_view sv{RSTRING_PTR(str),
                        static_cast<gitee::string_view::size_type>(len)};
  int64_t size = 0;
  if (!gitee::CalculateRepoSize(sv, size)) {
    return Qnil;
  }
  auto mb = (int)lround((double)size / MB);
  return LL2NUM(mb);
}

static VALUE enhance_escape_unicode(VALUE self, VALUE str) {
  (void)self;
  Check_Type(str, T_STRING);
  auto len = RSTRING_LEN(str);
  gitee::string_view sv{RSTRING_PTR(str),
                        static_cast<gitee::string_view::size_type>(len)};
  auto bs = gitee::EscapeUnicode(sv);
  return rb_str_new(bs.data(), bs.size());
}

static VALUE enhance_unescape_unicode(VALUE self, VALUE str) {
  (void)self;
  auto len = RSTRING_LEN(str);
  gitee::string_view sv{RSTRING_PTR(str),
                        static_cast<gitee::string_view::size_type>(len)};
  std::string s;
  if (gitee::UnescapeUnicode(sv, true, &s)) {
    return rb_str_new(s.data(), s.size());
  }
  return Qnil;
}

static VALUE enhance_safely_remove_repo(VALUE self, VALUE root, VALUE trash,
                                        VALUE diskpath) {
  (void)self;
  Check_Type(root, T_STRING);
  Check_Type(trash, T_STRING);
  Check_Type(diskpath, T_STRING);
  auto rlen = RSTRING_LEN(root);
  auto tlen = RSTRING_LEN(trash);
  auto dlen = RSTRING_LEN(diskpath);
  gitee::string_view sroot{RSTRING_PTR(root),
                           static_cast<gitee::string_view::size_type>(rlen)};
  gitee::string_view strash{RSTRING_PTR(trash),
                            static_cast<gitee::string_view::size_type>(tlen)};
  gitee::string_view sdiskpath{
      RSTRING_PTR(diskpath), static_cast<gitee::string_view::size_type>(dlen)};
  std::string trashdir;
  if (!gitee::MoveRepoToTrashEx(sroot, strash, sdiskpath, trashdir)) {
    enhance_exception_raise();
  }
  return rb_str_new(trashdir.data(), trashdir.size());
}

static VALUE enhance_remove_fetch_tmp_pack(VALUE self, VALUE gitdir) {
  (void)self;
  Check_Type(gitdir, T_STRING);
  auto dlen = RSTRING_LEN(gitdir);
  gitee::string_view dir{RSTRING_PTR(gitdir),
                         static_cast<gitee::string_view::size_type>(dlen)};
  if (!gitee::RemoveFetchTmpPack(dir)) {
    return Qfalse;
  }
  return Qtrue;
}

static VALUE enhance_remove_incoming_dir(VALUE self, VALUE gitdir) {
  (void)self;
  Check_Type(gitdir, T_STRING);
  auto dlen = RSTRING_LEN(gitdir);
  gitee::string_view dir{RSTRING_PTR(gitdir),
                         static_cast<gitee::string_view::size_type>(dlen)};
  if (!gitee::IncomingTrash(dir)) {
    return Qfalse;
  }
  return Qtrue;
}

static VALUE enhance_fast_validate_utf8(VALUE self, VALUE str) {
  (void)self;
  Check_Type(str, T_STRING);
  auto len = RSTRING_LEN(str);
  auto p = RSTRING_PTR(str);
  if (!gitee::FastValidateUTF8(p, static_cast<size_t>(len))) {
    return Qfalse;
  }
  return Qtrue;
}

static VALUE mEnhanceUtils;
// Initializers
extern "C" void Init_enhanceutils() {
  //
  mEnhanceUtils = rb_define_module("EnhanceUtils");
  // Encoding
  rb_define_module_function(mEnhanceUtils, "detect_buffer_encoding",
                            RUBY_METHOD_FUNC(enhance_detect_buffer_encoding),
                            1);
  rb_define_module_function(mEnhanceUtils, "repo_size",
                            RUBY_METHOD_FUNC(enhance_repo_size), 1);
  rb_define_module_function(mEnhanceUtils, "repo_size_mb",
                            RUBY_METHOD_FUNC(enhance_repo_size_mb), 1);
  rb_define_module_function(mEnhanceUtils, "escape_unicode",
                            RUBY_METHOD_FUNC(enhance_escape_unicode), 1);
  rb_define_module_function(mEnhanceUtils, "unescape_unicode",
                            RUBY_METHOD_FUNC(enhance_unescape_unicode), 1);
  rb_define_module_function(mEnhanceUtils, "safely_remove_repo",
                            RUBY_METHOD_FUNC(enhance_safely_remove_repo), 3);
  rb_define_module_function(mEnhanceUtils, "remove_fetch_tmp_pack",
                            RUBY_METHOD_FUNC(enhance_remove_fetch_tmp_pack), 1);
  rb_define_module_function(mEnhanceUtils, "remove_incoming_dir",
                            RUBY_METHOD_FUNC(enhance_remove_incoming_dir), 1);
  // Fast validate UTF8
  rb_define_module_function(mEnhanceUtils, "fast_validate_utf8",
                            RUBY_METHOD_FUNC(enhance_fast_validate_utf8), 1);
}
